<?php

namespace App\Exceptions;

use Exception;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Contracts\Support\Responsable;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\Exceptions\HttpResponseException;
use Illuminate\Http\Response;
use Illuminate\Validation\ValidationException;
use Laravel\Lumen\Exceptions\Handler as ExceptionHandler;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

class Handler extends ExceptionHandler
{
    /**
     * A list of the exception types that should not be reported.
     *
     * @var array
     */
    protected $dontReport = [
        AuthorizationException::class,
//        HttpException::class,
//        ModelNotFoundException::class,
        ValidationException::class,
    ];

    /**
     * Report or log an exception.
     *
     * This is a great spot to send exceptions to Sentry, Bugsnag, etc.
     *
     * @param  \Exception  $exception
     * @return void
     *
     * @throws \Exception
     */
    public function report(Exception $exception)
    {
//        parent::report($exception);
        if ($this->shouldntReport($exception)) {
            return;
        }

        if (method_exists($exception, 'report')) {
            $exception->report();
        }

        try {
            $logger = app(LoggerInterface::class);
        } catch (Exception $ex) {
            throw $exception; // throw the original exception
        }

        if (IsCli()) {
            $logger->channel('cliError')->error($exception->getMessage(), ['exception' => $exception]);
        } else {
            $logger->error($exception->getMessage(), ['exception' => $exception]);
        }
    }

    /**
     * Render an exception into an HTTP response.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Exception  $exception
     * @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse
     *
     * @throws \Exception
     */
    public function render($request, Exception $exception)
    {
        if (!env('IS_FORMAT_EXCEPTION', true)) {
            return parent::render($request, $exception);
        }

        $relData = "";
        if (method_exists($exception, 'render')) {
            $relData = $exception->render($request);
        } elseif ($exception instanceof Responsable) {
            $relData = $exception->toResponse($request);
        }

        if ($exception instanceof HttpResponseException) {
            $relData = $exception->getResponse();
        } elseif ($exception instanceof ModelNotFoundException) {
            $exception = new NotFoundHttpException($exception->getMessage(), $exception);
        } elseif ($exception instanceof AuthorizationException) {
            $exception = new HttpException(403, $exception->getMessage());
        } elseif ($exception instanceof ValidationException && $exception->getResponse()) {
            $relData = $exception->getResponse();
        }

        $code = method_exists($exception, 'getStatusCode') ? $exception->getStatusCode() : $exception->getCode();
        $newException = new Exception($relData !== "" ? json_encode($relData, JSON_UNESCAPED_UNICODE) : $exception->getMessage(), (int)$code);

        if (env('APP_DEBUG', true)) {
            $errorInfo = $this->handleErrorInfo($exception);
            $relData = ApiReturn($newException->getCode(), $newException->getMessage(), $errorInfo);
        } else {
            $relData = ApiReturn($exception->getCode(), $exception->getMessage());
        }
        return new Response($relData);
    }

    /**
     * 处理异常信息
     * @param Exception $exception
     * @return array
     */
    protected function handleErrorInfo(Exception $exception) {
        $allTraceList = $exception->getTrace();
        $relList = ["#error:" . $exception->getFile() . "(Line:". $exception->getLine() ."行):"];
        foreach ($allTraceList as $key => $trace) {
            $tempStr = "#" . $key . ":";
            if (array_key_exists('file', $trace)) {
                $tempStr .= $trace['file'] . " ";
            }

            if (array_key_exists('line', $trace)) {
                $tempStr .= '(Line:'.$trace['line'] . "行):";
            }

            if (array_key_exists("class", $trace)) {
                $tempStr .= $trace['class'];
            }

            if (array_key_exists("type", $trace)) {
                $tempStr .= $trace['type'];
            }

            if (array_key_exists("function", $trace)) {
                $tempStr .= $trace['function'] . "(";
                if (array_key_exists("args", $trace)) {
                    foreach ($trace["args"] as $tempArg) {
                        $tempStr .= json_encode($tempArg, 256) . ",";
                    }
                    $tempStr = rtrim($tempStr, ',');
                }
                $tempStr .= ")";
            }

            $relList[] = $tempStr;
        }
        return $relList;
    }
}
